package org.activequant.container.context;

import java.util.Map;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * SpringTradeSystemContext(extends TradeSystemContextBase implements ApplicationContextAware.<br/>
 * Holds the following associated variables:
 * <ul>
 * <li>context(ApplicationContext)</li>
 * <li>defaultBeans(DefaultBeanList)</li>
 * </ul>
 * Holds the following inherited variables:
 * <ul>
 * <li>tradeSystems(List&lt;ITradeSystemHandle&gt;)</li>
 * <li>isRunning(boolean)</li>
 * <li>factory(IObjectFactory)</li>
 * </ul>
 * <b>History:</b><br>
 *  - [15.01.2008] Created (Erik Nijkamp)<br>
 *
 *  @author Erik Nijkamp
 */
public final class SpringTradeSystemContext extends TradeSystemContextBase implements ApplicationContextAware {
	/**
	 * private ApplicationContext context;
	 */
	private ApplicationContext context;
	/**
	 * private DefaultBeanList defaultBeans;
	 */
	private DefaultBeanList defaultBeans;
	/**
	 * An empty SpringTradeSystemContext(extends TradeSystemContextBase implements ApplicationContextAware) constructor
	 */
	public SpringTradeSystemContext() {
		
	}
	/**
	 * Constructs a SpringTradeSystemContext(extends TradeSystemContextBase implements ApplicationContextAware) using the given context(ApplicationContext)
	 * to set its associated context(ApplicationContext).<br/>
	 * If the same context(ApplicationContext) has at least one Bean of type <code>DefaultBeanList.class</code>, it also sets its associated 
	 * defaultBeans(DefaultBeanList) with an instance of the first such bean.
	 * @param context
	 */
	public SpringTradeSystemContext(ApplicationContext context) {
		setApplicationContext(context);
	}
	/**
	 * Tries to find and return a bean(T) from the associated context(ApplicationContext) whose type and/or Id match the given clazz(Clazz&lt;T&gt;) and name(String).<br/>
	 * If it can't find any, returns null. If it finds one that matches the type, but the given name(String) is null, it returns that one.<br/>
	 * If it finds more that match the type, but the given name(String) is null, returns the first one that is a default Bean.<br/>
	 * Otherwise it returns the one that matches by type and name.
	 */
	@SuppressWarnings("unchecked")
	@Override
	protected <T> T lookup(Class<T> clazz, String name) {
		assert(context!=null);
		Map<String,T> services = context.getBeansOfType(clazz);
		if(services.size() == 0) {
			return null;
		} else if(name == null && services.size() == 1) {
			return services.values().iterator().next();
		} else if(name == null) {
			for(T object : services.values()) {
				if(isDefaultBean(object))
					return object;
			}			
			throw new IllegalArgumentException("ambiguity: following beans match class " + clazz.getSimpleName() + ": " + services.keySet());
		} else {
			// match by name
			return services.get(name);
		}
	}
	/**
	 * returns whether the associated defaultBeans(DefaultBeanList) is null, or (if not null) whether its objects(Set&lt;Object&gt;) contains the given object(Object)
	 * @param object
	 * @return
	 */
	private boolean isDefaultBean(Object object) {
		return defaultBeans == null ? true : defaultBeans.contains(object);
	}
	/**
	 * <strong>1.</strong> Sets the associated context(ApplicationContext) with the given context(ApplicationContext).<br/>
	 * <strong>2.</strong> If the same context(ApplicationContext) has at least one Bean of type <code>DefaultBeanList.class</code>,
	 * it sets the associated defaultBeans(DefaultBeanList) with an instance of the first such bean.
	 */
	public void setApplicationContext(ApplicationContext context) {
		this.context = context;
		lookupDefaultList(context);
	}
	/**
	 * If the given context(ApplicationContext) has at least one Bean of type <code>DefaultBeanList.class</code>,
	 * it sets the associated defaultBeans(DefaultBeanList) with an instance of the first such bean.
	 * @param context
	 */
	private void lookupDefaultList(ApplicationContext context) {
		if(!context.getBeansOfType(DefaultBeanList.class).isEmpty()) {
			defaultBeans = (DefaultBeanList) context.getBeansOfType(DefaultBeanList.class).values().iterator().next();
		}
	}
	
}
